home *** CD-ROM | disk | FTP | other *** search
/ Precision Software Appli…tions Silver Collection 1 / Precision Software Applications Silver Collection Volume One (PSM) (1993).iso / windows / games / wincapt.arj / WINCAP.C < prev    next >
C/C++ Source or Header  |  1992-07-07  |  55KB  |  1,751 lines

  1. //***********************************************************************
  2. //
  3. // Wincap.c
  4. //
  5. // Windows Screen Capture Utility
  6. // Version 3.10 (Second version)
  7. //
  8. // Description:
  9. // ------------
  10. //
  11. // Captures portions of the screen, specific windows, or the entire screen
  12. // and saves it to a file or prints it.  Uses DIBAPI functions to do most
  13. // of the capture/printing/saving work.  See the file DIBAPI.TXT for a
  14. // description of the DIB api functions.
  15. //
  16. // Development Team: Mark Bader
  17. //                   Patrick Schreiber
  18. //                   Garrett McAuliffe
  19. //                   Eric Flo
  20. //                   Tony Claflin
  21. //                   Dan Ruder
  22. //
  23. //
  24. // Changes from first version:
  25. //
  26. // - Updated to use 3.1 Common Dialogs and 3.1 hook functions
  27. // - New user interface displays window which was captured in client
  28. //      area of Wincap, complete with scroll bars
  29. // - Hot new hotkeys allow more versatile capturing of windows (e.g. you
  30. //      can now capture windows with menu items pulled down)
  31. // - New APIs to draw bitmaps and DIBs on the screen
  32. // - Nifty startup bitmap
  33. // - All DIB API functions are now in a DLL, which can be used
  34. //      by any application
  35. // 
  36. // Written by Microsoft Product Support Services, Developer Support.
  37. // Copyright (c) 1991 Microsoft Corporation. All rights reserved.
  38. //***********************************************************************
  39.  
  40. #include <windows.h>
  41. #include <string.h>
  42. #include "commdlg.h"
  43. #include "wincap.h"
  44. #include "dialogs.h"
  45. #include "dibapi.h"
  46.  
  47. char szAppName[20];     // Name of application - used in dialog boxes
  48.  
  49. /* Global variables */
  50. HWND     ghInst;         // Handle to instance
  51. HWND     ghWndMain;      // Handle to main window
  52. FARPROC  lpfnKeyHook;    // Used in keyboard hook
  53. FARPROC  lpfnOldHook;    // Used for keyboard hook
  54. HWND     hModelessDlg;   // Handle to modeless "Saving to file..." dialog box
  55.  
  56. BOOL     bStartup=TRUE;  // Startup flag for WM_PAINT/logo
  57. BOOL     bViewFull=FALSE;// Full view flag
  58. HBITMAP  ghbmLogo;       // Handle to logo bitmap
  59. HBITMAP  ghBitmap=NULL;  // Handle to captured bitmap
  60. HPALETTE ghPal=NULL;     // Handle to our bitmap's palette
  61. char     gszWindowText[100];// Text which tells what we captured
  62.  
  63. #define WM_DOCAPTURE WM_USER+101 // Used for screen capture messages
  64.  
  65. /* Macro to swap two values */
  66. #define SWAP(x,y)   ((x)^=(y)^=(x)^=(y))
  67. #define SCROLL_RATIO    4
  68.  
  69.  
  70. //************************************************************************
  71. //
  72. // WinMain()
  73. //
  74. // Entry point of the Application.
  75. //
  76. // History:   
  77. //            
  78. //   Date      Author       Reason         
  79. //   9/15/91   Mark Bader   Created         
  80. //   12/11/91  Mark Bader   Made main window be normal rather
  81. //                            than keep iconic
  82. //   03/04/92  Mark Bader   Changed keyboard hook to 3.1 spec - e.g. put
  83. //                            system-wide hook func in a DLL
  84. //
  85. //************************************************************************
  86.  
  87. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine,
  88.                    int nCmdShow)
  89. {
  90.    MSG msg;
  91.    WNDCLASS wndclass;
  92.    HWND hWnd;
  93.  
  94.    strcpy(szAppName, "WinCap");     // Name of our App
  95.    hModelessDlg = NULL;             // Set handle to modeless dialog to NULL because
  96.                                     // we haven't created it yet
  97.    if (!hPrevInstance)
  98.    {
  99.       wndclass.style = 0;
  100.       wndclass.lpfnWndProc = (WNDPROC)WndProc;
  101.       wndclass.cbClsExtra = 0;
  102.       wndclass.cbWndExtra = 0;
  103.       wndclass.hInstance = hInstance;
  104.       wndclass.hIcon = LoadIcon(hInstance, "WINCAP");
  105.       wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  106.  
  107.       // Use black for background for better contrast
  108.       wndclass.hbrBackground = GetStockObject(GRAY_BRUSH);
  109.  
  110.       wndclass.lpszMenuName = (LPSTR)"MAINMENU";
  111.       wndclass.lpszClassName = (LPSTR)szAppName;
  112.       if (!RegisterClass(&wndclass))
  113.          return FALSE;
  114.       ghInst = hInstance;  // Set Global variable
  115.  
  116.       // Create a main window for this application instance.
  117.  
  118.       hWnd = CreateWindow(szAppName,            // Name of the window's class
  119.                           "Screen Capture",     // Text for window caption
  120.                           WS_OVERLAPPEDWINDOW,  // Window Style
  121.                           CW_USEDEFAULT,        // Default horizontal position
  122.                           CW_USEDEFAULT,        // Default vertical position
  123.                           450,        // Default width & height -- this is
  124.                           345,        // big enough to contain startup bitmap
  125.                           NULL,                 // Overlapped windows have no parent
  126.                           NULL,                 // Use the window class menu
  127.                           hInstance,            // This instance owns this window
  128.                           NULL);                // Pointer (not used)
  129.       ghWndMain = hWnd;      // Set global variable
  130.  
  131.       ShowWindow(hWnd, nCmdShow);
  132.       UpdateWindow(hWnd);
  133.  
  134.       /*
  135.        * Set up the Keyboard hook for our hotkeys
  136.        */
  137.  
  138.       InstallHook(hWnd, TRUE);  // Function resides in DIBAPI.DLL
  139.  
  140.       /* Create our full-screen view class */
  141.  
  142.       wndclass.style = 0;
  143.       wndclass.lpfnWndProc = (WNDPROC)FullViewWndProc;
  144.       wndclass.cbClsExtra = 0;
  145.       wndclass.cbWndExtra = 0;
  146.       wndclass.hInstance = hInstance;
  147.       wndclass.hIcon = NULL;
  148.       wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  149.       wndclass.hbrBackground = GetStockObject(GRAY_BRUSH);
  150.       wndclass.lpszMenuName = (LPSTR)NULL;
  151.       wndclass.lpszClassName = (LPSTR)"ViewClass";
  152.       if (!RegisterClass(&wndclass))
  153.          return FALSE;
  154.  
  155.    }
  156.  
  157.    /*
  158.     * Let's make this a single-instance app -- we can get into hotkey
  159.     * conflicts (e.g. windows won't know which instance of WINCAP to 
  160.     * send the message to).  
  161.     */
  162.    if (hPrevInstance)
  163.    {
  164.       MessageBox(NULL, "WinCap is already running.  "
  165.                        "You cannot start multiple copies of WinCap.", szAppName,
  166.                  MB_OK | MB_ICONHAND);
  167.       return FALSE;
  168.    }
  169.  
  170.    /* Polling messages from event queue -- we have a modeless dialog
  171.       box, so we have to take care of the messages here also */
  172.  
  173.    while (GetMessage(&msg, NULL, 0, 0))
  174.    {
  175.       if (hModelessDlg == NULL || !IsDialogMessage(hModelessDlg, &msg))
  176.       {
  177.          TranslateMessage(&msg);
  178.          DispatchMessage(&msg);
  179.       }
  180.    }
  181.    return msg.wParam;
  182. }
  183.  
  184.  
  185.  
  186. //**********************************************************************
  187. //
  188. // WndProc()
  189. //
  190. // This is our main window procedure.  It receives all the messages destined
  191. // for our application's main window.
  192. //
  193. // When we capture a window, we capture it into a Device-Dependent bitmap,
  194. // and at the same time, we get a copy of the current system palette.  This
  195. // makes displaying the bitmap on the screen very fast.  And when we want
  196. // to print or save the captured window, we need to use this palette to
  197. // convert the DDB to a DIB.
  198. //
  199. // History:   
  200. // 
  201. //   Date      Author          Reason
  202. //   9/15/91   Mark Bader      Created
  203. //   12/12/91  Mark Bader      Added code for new menu items
  204. //   12/13/91  Pat Schreiber   Added painting of captured DIB in
  205. //                                client area
  206. //   12/16/91  Pat Schreiber   Added support for full-screen view
  207. //   12/20/91  Pat Schreiber   Added support for palette changes
  208. //                               and scroll bars
  209. //   01/03/92  Mark Bader      Added bunches of comments
  210. //
  211. //*********************************************************************
  212.  
  213. long FAR PASCAL WndProc(HWND hWnd, WORD wMessage, WORD wParam, LONG lParam)
  214. {
  215.    /*
  216.     * The bNowCapturing variable is set to TRUE if we are in the middle of
  217.     * printing.  This takes care of the case when the user presses the hotkey
  218.     * during capturing
  219.     */
  220.  
  221.    static BOOL bNowCapturing = FALSE;
  222.    static BOOL bCapturedYet = FALSE;  // TRUE if window contains captured screen
  223.    HWND   hViewWnd;                   // Handle to our view window
  224.  
  225.    switch (wMessage)
  226.       {
  227.  
  228.    case WM_CREATE:
  229.       ghbmLogo = LoadBitmap(ghInst, "STARTBMP");
  230.       break;
  231.  
  232.  
  233.    // Gray out the "File.Save" menu item if we haven't
  234.    // captured anything yet.
  235.  
  236.    case WM_INITMENU:
  237.      EnableMenuItem(GetMenu(hWnd),
  238.                     IDM_SAVE,
  239.                     MF_BYCOMMAND |
  240.                     (bCapturedYet ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
  241.      EnableMenuItem(GetMenu(hWnd),
  242.                     IDM_PRINT,
  243.                     MF_BYCOMMAND |
  244.                     (bCapturedYet ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
  245.      return 0;
  246.  
  247.  
  248.    // The WM_PALETTECHANGED message informs all windows that the window with
  249.    // input focus has realized its logical palette, thereby changing the
  250.    // system palette. This message allows a window without input focus that
  251.    // uses a color palette to realize its logical palettes and update its
  252.    // client area.
  253.    //
  254.    // This message is sent to all windows, including the one that changed
  255.    // the system palette and caused this message to be sent. The wParam of
  256.    // this message contains the handle of the window that caused the system
  257.    // palette to change. To avoid an infinite loop, care must be taken to
  258.    // check that the wParam of this message does not match the window's
  259.    // handle.
  260.  
  261.    case WM_PALETTECHANGED:
  262.       {
  263.       HDC      hDC;      // Handle to device context
  264.       HPALETTE hOldPal;  // Handle to previous logical palette
  265.  
  266.       // Before processing this message, make sure we
  267.       // are indeed using a palette
  268.       if (ghPal)
  269.          {
  270.          // If this application did not change the palette, select
  271.          // and realize this application's palette
  272.          if (wParam != hWnd)
  273.             {
  274.             // Need the window's DC for SelectPalette/RealizePalette
  275.             hDC = GetDC(hWnd);
  276.  
  277.             // Select and realize our palette
  278.             hOldPal = SelectPalette(hDC, ghPal, FALSE);
  279.             RealizePalette(hDC);
  280.  
  281.             // WHen updating the colors for an inactive window,
  282.             // UpdateColors can be called because it is faster than
  283.             // redrawing the client area (even though the results are
  284.             // not as good)
  285.             UpdateColors(hDC);
  286.  
  287.             // Clean up
  288.             if (hOldPal)
  289.                SelectPalette(hDC, hOldPal, FALSE);
  290.             ReleaseDC(hWnd, hDC);
  291.             }
  292.          }
  293.       }
  294.       break;
  295.  
  296.  
  297.    // The WM_QUERYNEWPALETTE message informs a window that it is about to
  298.    // receive input focus. In response, the window receiving focus should
  299.    // realize its palette as a foreground palette and update its client
  300.    // area. If the window realizes its palette, it should return TRUE;
  301.    // otherwise, it should return FALSE.
  302.  
  303.    case WM_QUERYNEWPALETTE:
  304.       {
  305.       HDC      hDC;      // Handle to device context
  306.       HPALETTE hOldPal;  // Handle to previous logical palette
  307.  
  308.       // Before processing this message, make sure we
  309.       // are indeed using a palette
  310.       if (ghPal)
  311.          {
  312.          // Need the window's DC for SelectPalette/RealizePalette
  313.          hDC = GetDC(hWnd);
  314.  
  315.          // Select and realize our palette
  316.          hOldPal = SelectPalette(hDC, ghPal, FALSE);
  317.          RealizePalette(hDC);
  318.  
  319.          // Redraw the entire client area
  320.          InvalidateRect(hWnd, NULL, TRUE);
  321.          UpdateWindow(hWnd);
  322.  
  323.          // Clean up
  324.          if (hOldPal)
  325.             SelectPalette(hDC, hOldPal, FALSE);
  326.          ReleaseDC(hWnd, hDC);
  327.  
  328.          // Message processed, return TRUE
  329.          return TRUE;
  330.          }
  331.       // Message not processed, return FALSE
  332.       return FALSE;
  333.       }
  334.       break;
  335.  
  336.  
  337.    case WM_SIZE:
  338.       {
  339.       static BOOL bSizing=FALSE;
  340.  
  341.       // Check if we are already sizing
  342.       if (bSizing)
  343.          return NULL;
  344.  
  345.       bSizing = TRUE;
  346.       DoSize(hWnd);
  347.       bSizing = FALSE;
  348.       }
  349.       break;
  350.  
  351.  
  352.    case WM_HSCROLL:
  353.    case WM_VSCROLL:
  354.       DoScroll(hWnd, wMessage, LOWORD(lParam), wParam);
  355.       break;
  356.  
  357.  
  358.    case WM_PAINT:
  359.       DoPaint(hWnd);
  360.       break;
  361.  
  362.  
  363.    case WM_COMMAND:
  364.       switch (wParam)
  365.          {
  366.          case IDM_ABOUT:
  367.  
  368.       /*
  369.        * Display "About" Box
  370.        */
  371.       {
  372.          FARPROC lpfnDIALOGSMsgProc;
  373.  
  374.          lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)AboutDlgProc, ghInst);
  375.          DialogBox(ghInst, (LPSTR)"About", hWnd, lpfnDIALOGSMsgProc);
  376.          FreeProcInstance(lpfnDIALOGSMsgProc);
  377.       }
  378.          break;
  379.  
  380.       case IDM_SAVE:
  381.         SaveMe();
  382.         break;
  383.  
  384.       case IDM_PRINT:
  385.         PrintMe();
  386.         break;
  387.  
  388.       case IDM_VIEWFULL:
  389.          if (!bViewFull && (ghBitmap || bStartup))
  390.             {
  391.             HDC hDC;
  392.             int ScreenX, ScreenY;
  393.  
  394.             hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  395.             ScreenX = GetDeviceCaps(hDC, HORZRES);
  396.             ScreenY = GetDeviceCaps(hDC, VERTRES);
  397.             DeleteDC(hDC);
  398.  
  399.             hViewWnd = CreateWindow((LPSTR)"ViewClass",
  400.                                    (LPSTR)NULL,
  401.                                    WS_POPUP | WS_VISIBLE,
  402.                                    0,
  403.                                    0,
  404.                                    ScreenX,
  405.                                    ScreenY,
  406.                                    hWnd,
  407.                                    NULL,
  408.                                    ghInst,
  409.                                    NULL);
  410.  
  411.             ShowWindow(hViewWnd, SW_SHOW);
  412.             UpdateWindow(hViewWnd);
  413.             }
  414.          else
  415.             MessageBox(hWnd, (LPSTR)"No image to view!",
  416.                        (LPSTR)"View Full Screen", MB_OK);
  417.          break;
  418.  
  419.       case IDM_VIEWCLEAR:
  420.          {
  421.          // If we are just displaying logo, don't
  422.          // display it anymore
  423.          if (bStartup)
  424.             bStartup = FALSE;
  425.  
  426.          // Delete captured bitmap if we have one
  427.          if (ghBitmap)
  428.             {
  429.             DeleteObject(ghBitmap);
  430.             ghBitmap = NULL;
  431.             }
  432.  
  433.          // Delete our captured bitmap's palette if we have one
  434.          if (ghPal)
  435.             {
  436.             DeleteObject(ghPal);
  437.             ghPal = NULL;
  438.             }
  439.  
  440.          /* Now update display to reflect fact that we
  441.           * nuked the captured bitmap or don't want to
  442.           * look at the cool logo
  443.           */
  444.          InvalidateRect(hWnd, NULL, TRUE);
  445.          UpdateWindow(hWnd);
  446.  
  447.          bCapturedYet = FALSE;  // Gray out "File.Save" menu item
  448.          }
  449.          break;
  450.  
  451.       case IDM_CAPTWINDOW:
  452.       case IDM_CAPTCLIENT:
  453.       case IDM_CAPTRECT:
  454.       case IDM_ACTIVEWINDOW:
  455.       case IDM_DESKTOP:
  456.  
  457.          /*
  458.           * User selected one of the window capture items
  459.           */
  460.  
  461.          /* Check to see that we aren't already in the middle of capturing
  462.           * This could happen if the user presses our hotkey in the middle of
  463.           * one of our dialog boxes.
  464.           */
  465.  
  466.           if (bNowCapturing)
  467.           {
  468.              MessageBox(NULL, "Already capturing.  Try again later.",
  469.                         szAppName, MB_OK | MB_ICONEXCLAMATION);
  470.           }
  471.           else
  472.           {
  473.              // User wants to capture screen.  One problem we may
  474.              // run into here is that we might have a popup menu pulled
  475.              // down in our own application when we get to this point
  476.              // (because of the hotkey feature of this app).
  477.              //
  478.              // Normally, we'd just enter a message loop after calling
  479.              // ShowWindow(SW_HIDE) to take care of any menu messages
  480.              // which may have been posted as we hide our application's
  481.              // window, and *then* call our screen capture routine.
  482.              // But unfortunately, we can't do that here and be 100% safe.
  483.              //
  484.              // If we *have* been sent here on a hotkey, and a menu of
  485.              // our own app is currently down, then we are currently running
  486.              // inside of a PeekMessage() loop in the Windows Menu
  487.              // manager. We should *not* enter another PeekMessage loop,
  488.              // but should return from this message case right away.
  489.              // The Windows Menu manager code relies on checking messages
  490.              // in it's queue via it's own PeekMessage loop, and if we
  491.              // entered one, it would confuse the menu manager.
  492.              //
  493.              // So what we do instead is just post ourselves a private
  494.              // message, and when we get this message (see below), then
  495.              // do the screen capture.
  496.              //
  497.              // Note: Under the 3.1 Debug Kernel, if we get here when
  498.              // a menu is down in our app, you should expect the following
  499.              // warning:
  500.              //
  501.              // wn WINCAP USER: Exiting menu mode: another window activated
  502.              //
  503.  
  504.              // Commence screen capture!
  505.              bNowCapturing = TRUE;
  506.  
  507.              ShowWindow(hWnd, SW_HIDE);  // Hide main app's window
  508.  
  509.              // Allow this message case to return right away.  We'll
  510.              // capture screen down below.
  511.              PostMessage(hWnd, WM_DOCAPTURE, wParam, 0L);
  512.          }
  513.          break;
  514.  
  515.       case IDM_EXIT:
  516.           PostMessage(hWnd, WM_CLOSE, 0, 0L);
  517.           break;
  518.  
  519.       default:
  520.           return DefWindowProc(hWnd, wMessage, wParam, lParam);
  521.           break;
  522.         }
  523.         break;
  524.  
  525.  
  526.      // Message case for doing screen capture.  This message is posted to
  527.      // us from the IDM_CAPT* code above.  wParam should be equal to the
  528.      // ID of the message which we got sent here for (it is used in the
  529.      // call to DoCapture()).
  530.      case WM_DOCAPTURE:
  531.      {
  532.      // We're going to capture a new screen, get rid of
  533.      // previous captured image
  534.  
  535.      if (ghBitmap) {
  536.         DeleteObject(ghBitmap);
  537.         ghBitmap = NULL;
  538.         }
  539.      if (ghPal) {
  540.         DeleteObject(ghPal);
  541.         ghPal = NULL;
  542.         }
  543.  
  544.      // Save captured screen as bitmap
  545.      DoCapture(hWnd, wParam);
  546.  
  547.      // Un-hide Window
  548.      ShowWindow(hWnd, SW_SHOW);
  549.  
  550.      bNowCapturing = FALSE;
  551.      bCapturedYet = TRUE; // Enable "File.Save" menu item
  552.      }
  553.      break;
  554.  
  555.  
  556.    case WM_DESTROY:
  557.       /*
  558.        * Clean up
  559.        */
  560.  
  561.       InstallHook(hWnd, FALSE);  // Remove keyboard hook
  562.  
  563.       DeleteObject(ghbmLogo);
  564.       if (ghBitmap)
  565.          DeleteObject(ghBitmap);
  566.       if (ghPal)
  567.          DeleteObject(ghPal);
  568.  
  569.       PostQuitMessage(0);
  570.       break;
  571.  
  572.    default:
  573.       return DefWindowProc(hWnd, wMessage, wParam, lParam);
  574.       }
  575.    return 0L;
  576. }
  577.  
  578.  
  579. //**********************************************************************
  580. //
  581. // FullViewWndProc()
  582. //
  583. // This is our full-screen popup window procedure. It is used to display an
  584. // image using the entire screen. Clicking the left mouse button restores
  585. // to the main app window.
  586. //
  587. //
  588. // History:   Date      Author              Reason
  589. //            12/16/91  Patrick Schreiber   Created
  590. //
  591. //*********************************************************************
  592.  
  593. long FAR PASCAL FullViewWndProc(HWND  hWnd,
  594.                                 WORD  wMessage,
  595.                                 WORD  wParam,
  596.                                 LONG  lParam)
  597. {
  598.    switch (wMessage)
  599.       {
  600.       case WM_PAINT:
  601.          {
  602.          PAINTSTRUCT ps;
  603.          HDC         hMemDC;
  604.          BITMAP      bm;
  605.          HBITMAP     hOldBm;
  606.          RECT        rect, rectClient;
  607.          int         x, y;
  608.  
  609.          BeginPaint(hWnd, &ps);
  610.  
  611.          // Check to see if we are displaying a bitmap
  612.          if (!ghBitmap)
  613.             {
  614.  
  615.             // No bitmap yet, are we in start mode?
  616.             if (bStartup)
  617.                {
  618.                GetClientRect(hWnd, &rectClient);
  619.  
  620.                hMemDC = CreateCompatibleDC(ps.hdc);
  621.  
  622.                // Select our logo bitmap
  623.                hOldBm = SelectObject(hMemDC, ghbmLogo);
  624.  
  625.                GetObject(ghbmLogo, sizeof(BITMAP), (VOID FAR *)&bm);
  626.  
  627.                x = (rectClient.right - bm.bmWidth) / 2;
  628.                y = (rectClient.bottom - bm.bmHeight) / 2;
  629.  
  630.                // Now bitblt our logo to client area
  631.                BitBlt(ps.hdc, x, y, bm.bmWidth, bm.bmHeight,
  632.                       hMemDC, 0, 0, SRCCOPY);
  633.  
  634.                // Clean up
  635.                SelectObject(hMemDC,hOldBm);
  636.                DeleteDC(hMemDC);
  637.                }
  638.             }
  639.          else
  640.             {
  641.             /* Get info for captured bitmap */
  642.             GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
  643.  
  644.             /* Fill in src/dest rectangle with width and height
  645.              * of captured bitmap
  646.              */
  647.             rect.left = 0;
  648.             rect.top = 0;
  649.             rect.right = bm.bmWidth;
  650.             rect.bottom = bm.bmHeight;
  651.  
  652.             /* Paint the captured bitmap in the client area */
  653.             PaintBitmap(ps.hdc, &rect, ghBitmap, &rect, ghPal);
  654.             }
  655.  
  656.          EndPaint(hWnd, &ps);
  657.          }
  658.          break;
  659.  
  660.       case WM_KEYDOWN:
  661.       case WM_LBUTTONDOWN:
  662.          DestroyWindow(hWnd);
  663.          break;
  664.  
  665.       default:
  666.          return DefWindowProc(hWnd, wMessage, wParam, lParam);
  667.       }
  668. }
  669.  
  670.  
  671. //**********************************************************************
  672. //
  673. // SaveMe()
  674. //
  675. // This procedure calls up the common dialog "File.Save" box, then 
  676. // saves the current hBitmap as a DIB in the specified file, in the
  677. // file format specified by the user in the dialog.
  678. //
  679. // History:   
  680. //
  681. //    Date      Author         Reason        
  682. //    12/19/91  Mark Bader     Created       
  683. //    03/05/92  Mark Bader     Changed to use File/Save Common Dialog
  684. //
  685. //*********************************************************************
  686.  
  687. void SaveMe()
  688.  
  689. {
  690.     char szFileBuf[255];    // Buffer to hold returned file name
  691.     DWORD dwFlags;          // used to pass in / get out file type
  692.     HDC hDC;                // HDC for getting info
  693.     int iBits;              // Bits per pixel of the display adapter
  694.     DWORD dCompression;     // Compression that the user specifies
  695.     WORD wBitCount;         // Bits per pixel that the user specifies
  696.     WORD wCompression;      // Compression
  697.     HDIB hDIB;              // A handle to our dib
  698.     FARPROC lpfnDIALOGSMsgProc; // procedure for "Saving" dialog
  699.     BOOL bResult;           // Result of dialog box - TRUE if OK was pressed
  700.  
  701.     // Set up default compression to display in dialog box
  702.  
  703.     wCompression = IDD_RGB;
  704.  
  705.     // Depending on bits/pixel type for current display,
  706.     // set the appropriate bit in the fFileOptions flag
  707.     //
  708.     // NOTE that we don't just assign wBitCount to iBits.  The reason
  709.     // for this is some displays aren't 1,4,8 or 24 bits.  Some are
  710.     // 15 bits, which isn't valid for a DIB, so in this case, we would
  711.     // set the bits to 24. 
  712.  
  713.     hDC = CreateDC("DISPLAY",NULL,NULL,NULL);
  714.     iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
  715.     DeleteDC(hDC);
  716.  
  717.     if (iBits <= 1)
  718.        wBitCount = 1;
  719.  
  720.     else if (iBits <= 4)
  721.        wBitCount = 4;
  722.  
  723.     else if (iBits <= 8)
  724.        wBitCount = 8;
  725.  
  726.     else if (iBits <= 24)
  727.        wBitCount = 24;
  728.  
  729.     // Our dwFlags parameter to GetFileName is made up of the
  730.     // bits per pixel in the HIWORD (1, 2, 4, or 24), and the compression
  731.     // type in the LOWORD (IDD_RGB, IDD_RLE4, or IDD_RLE8).
  732.  
  733.     dwFlags = MAKELONG(wCompression, wBitCount);
  734.  
  735.  
  736.     // Bring up File/Save... dialog and get info. about filename,
  737.     // compression, and bits/pix. of DIB to be written.
  738.  
  739.     bResult = GetFileName(ghWndMain, (LPSTR)szFileBuf, &dwFlags);
  740.  
  741.     // Extract DIB specs and save to file (if the user did not 
  742.     // press cancel)
  743.  
  744.       if (bResult) {
  745.          switch(LOWORD(dwFlags)) {
  746.             case IDD_RLE4:
  747.                 dCompression = BI_RLE4;
  748.                 break;
  749.             case IDD_RLE8:
  750.                 dCompression = BI_RLE8;
  751.                 break;
  752.             case IDD_RGB:
  753.             default:
  754.                 dCompression = BI_RGB;
  755.                 break;
  756.             }
  757.  
  758.         // First, call up a modeless dialog box which tells that we are
  759.         // saving this to a file...
  760.  
  761.         if (!hModelessDlg) {
  762.             lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)SavingDlgProc,
  763.                                                   ghInst);
  764.             hModelessDlg = CreateDialogParam(ghInst,
  765.                     (LPSTR)"Saving",ghWndMain,lpfnDIALOGSMsgProc,
  766.                     (DWORD)(LPSTR)szFileBuf);
  767.             }
  768.  
  769.         // Now, write out the DIB in the proper format.  The following
  770.         // API ChangeBitmapFormat() will convert the specified bitmap
  771.         // to a DIB with the specified Bit Count, Compression and
  772.         // Palette.  Remember that the HIWORD of dwFlags specifies the
  773.         // bits per pixel.
  774.  
  775.         hDIB = ChangeBitmapFormat(ghBitmap, HIWORD(dwFlags), 
  776.                         dCompression, ghPal);
  777.  
  778.         if (hDIB) {
  779.           if (SaveDIB(hDIB, szFileBuf))
  780.               MessageBox(NULL, "Could not save bitmap", "SaveDIB error",
  781.                          MB_ICONEXCLAMATION);
  782.  
  783.           DestroyDIB(hDIB);
  784.           }
  785.  
  786.         DestroyWindow(hModelessDlg);
  787.         FreeProcInstance(lpfnDIALOGSMsgProc);
  788.         hModelessDlg = NULL;
  789.       }
  790.  
  791. }
  792.  
  793. //*********************************************************************
  794. //
  795. // PrintMe()
  796. //
  797. // This procedure calls up the "File.Print" dialog, then prints the
  798. // current hBitmap as a DIB on the default printer.
  799. //
  800. // History:   Date      Author       Reason
  801. //            12/19/91  Mark Bader   Created
  802. //            03/12/92  Mark Bader   Couldn't convert to PrintDlg() common
  803. //                                   dialog, because of the API we use
  804. //
  805. //*********************************************************************
  806.  
  807. void PrintMe() {
  808.   static OPTIONSTRUCT opts;
  809.   FARPROC lpMsgProc;
  810.   int iReturn;
  811.   HDIB hDIB;
  812.   WORD wReturn;
  813.  
  814.   /*
  815.    * Display "Print Options" Box
  816.    */
  817.  
  818.    lpMsgProc = MakeProcInstance((FARPROC)PrintDlgProc, ghInst);
  819.    iReturn = DialogBoxParam(ghInst, (LPSTR)"Print",
  820.                             ghWndMain, lpMsgProc, (LONG)(LPSTR)&opts);
  821.    FreeProcInstance(lpMsgProc);
  822.  
  823.    if (iReturn) {
  824.  
  825.      // User pressed "OK" -- do the printing
  826.  
  827.      hDIB = BitmapToDIB(ghBitmap, ghPal);
  828.  
  829.      if (hDIB) {
  830.  
  831.        // Print the dib using PrintDIB() API
  832.  
  833.        if (opts.iOption == IDC_STRETCHTOPAGE)
  834.          wReturn = PrintDIB(hDIB, PW_STRETCHTOPAGE,
  835.                             0, 0, (LPSTR)gszWindowText);
  836.  
  837.        else if (opts.iOption == IDC_SCALE)
  838.          wReturn = PrintDIB(hDIB, PW_SCALE,
  839.                             opts.iXScale, opts.iYScale,
  840.                             (LPSTR)gszWindowText);
  841.        else
  842.          wReturn = PrintDIB(hDIB, PW_BESTFIT,
  843.                             0, 0, (LPSTR)gszWindowText);
  844.  
  845.        if (wReturn)
  846.            DIBError(wReturn);
  847.  
  848.        DestroyDIB(hDIB);
  849.        }
  850.     }
  851.  
  852.  
  853. }
  854.  
  855.  
  856.  
  857. //*********************************************************************
  858. //
  859. // DoCapture()
  860. //
  861. // This procedure gets called when the user wants to capture the
  862. // screen.  The wCommand parameter tells us which capture operation
  863. // we want to perform.
  864. //
  865. // History:   Date      Author         Reason
  866. //            9/15/91   Mark Bader     Created
  867. //
  868. //*********************************************************************
  869.  
  870.  
  871. void DoCapture(HWND hWnd, WORD wCommand)
  872. {
  873.    HBITMAP hBitmap;                // Handle to our temporary bitmap
  874.    HPALETTE hPal;                  // Handle to our palette
  875.  
  876.  
  877.    switch (wCommand) {
  878.  
  879.       case IDM_DESKTOP:
  880.  
  881.       /*
  882.        * Copy Entire screen to DIB
  883.        */
  884.  
  885.       {
  886.          RECT rScreen;       // Rect containing entire screen coordinates
  887.          HDC hDC;            // DC to screen
  888.          MSG msg;            // Message for the PeekMessage()
  889.  
  890.          hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  891.          rScreen.left = rScreen.top = 0;
  892.          rScreen.right = GetDeviceCaps(hDC, HORZRES);
  893.          rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
  894.  
  895.          /* Delete our DC */
  896.          DeleteDC(hDC);
  897.  
  898.          strcpy(gszWindowText, "Entire screen");
  899.  
  900.          // Wait until everybody repaints themselves
  901.  
  902.          while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0) {
  903.                 TranslateMessage(&msg);
  904.                 DispatchMessage(&msg);
  905.                 }
  906.  
  907.  
  908.          hBitmap = CopyScreenToBitmap(&rScreen);
  909.  
  910.          if (hBitmap)
  911.             hPal = GetSystemPalette();
  912.          else
  913.             hPal = NULL;
  914.       }
  915.          break;
  916.  
  917.  
  918.       case IDM_CAPTRECT:
  919.       /*
  920.        * Copy user-selected portion of screen to DIB
  921.        */
  922.  
  923.       {
  924.          RECT rRubberBand;       // Region to capture (screen coordinates)
  925.  
  926.          /*
  927.           * Allow user to "rubberband" a section of the screen for
  928.           * us to capture
  929.           */
  930.  
  931.          RubberBandScreen(&rRubberBand);
  932.          strcpy(gszWindowText, "User selected portion");
  933.  
  934.          hBitmap = CopyScreenToBitmap(&rRubberBand);
  935.  
  936.          if (hBitmap)
  937.             hPal = GetSystemPalette();
  938.          else
  939.             hPal = NULL;
  940.       }
  941.          break;
  942.  
  943.  
  944.  
  945.       case IDM_CAPTWINDOW:
  946.       case IDM_CAPTCLIENT:
  947.       case IDM_ACTIVEWINDOW:
  948.       {
  949.          HWND hWndSelect;       // The current active window
  950.          HWND hWndDesktop;      // Window to desktop
  951.          MSG msg;               // For our peekmessage loop
  952.  
  953.          if (wCommand == IDM_ACTIVEWINDOW)
  954.  
  955.             // Just capture the current active window, whatever it is
  956.  
  957.             hWndSelect = GetActiveWindow();
  958.  
  959.          else
  960.  
  961.             // Allow the user to click on a single window to capture
  962.  
  963.             hWndSelect = SelectWindow();
  964.  
  965.  
  966.           // If they try to capture the desktop window, then just
  967.           // capture the entire screen.
  968.  
  969.          hWndDesktop = GetDesktopWindow();
  970.  
  971.          if (hWndSelect == hWndDesktop)
  972.          {
  973.              RECT rScreen;       // Rect containing entire screen coordinates
  974.              HDC hDC;            // DC to screen
  975.              MSG msg;            // Message for the PeekMessage()
  976.  
  977.              hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  978.              rScreen.left = rScreen.top = 0;
  979.              rScreen.right = GetDeviceCaps(hDC, HORZRES);
  980.              rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
  981.  
  982.              /* Delete our DC */
  983.              DeleteDC(hDC);
  984.  
  985.              strcpy(gszWindowText, "Entire screen");
  986.  
  987.              // Wait until everybody repaints themselves
  988.  
  989.              while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0) {
  990.                     TranslateMessage(&msg);
  991.                     DispatchMessage(&msg);
  992.                     }
  993.  
  994.              ghBitmap = CopyScreenToBitmap(&rScreen);
  995.  
  996.              if (ghBitmap)
  997.                 ghPal = GetSystemPalette();
  998.              else
  999.                 ghPal = NULL;
  1000.  
  1001.             return;
  1002.          }
  1003.  
  1004.          // Check to see that the hWnd is not NULL
  1005.  
  1006.          if (!hWndSelect)
  1007.          {
  1008.             MessageBox(NULL, "Cannot capture that window!", szAppName,
  1009.                        MB_ICONEXCLAMATION | MB_OK);
  1010.  
  1011.             hBitmap = NULL;
  1012.             hPal = NULL;
  1013.             break;
  1014.          }
  1015.  
  1016.          /*
  1017.           * Make sure it's not a hidden window.  Hmm, capturing a hidden
  1018.           * window would certainly be a cool trick, wouldn't it?
  1019.           */
  1020.  
  1021.          if (!IsWindowVisible(hWndSelect))
  1022.          {
  1023.             MessageBox(NULL, "Window is not visible.  Can't capture",
  1024.                        szAppName, MB_ICONEXCLAMATION | MB_OK);
  1025.  
  1026.             hBitmap = NULL;
  1027.             hPal = NULL;
  1028.             break;
  1029.          }
  1030.  
  1031.          // Move window which was selected to top of Z-order for
  1032.          // the capture, and make it redraw itself
  1033.  
  1034.          SetWindowPos(hWndSelect, NULL, 0, 0, 0, 0,
  1035.                       SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE);
  1036.  
  1037.          UpdateWindow(hWndSelect);
  1038.  
  1039.          // Wait until everybody repaints themselves
  1040.  
  1041.          while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0) {
  1042.                 TranslateMessage(&msg);
  1043.                 DispatchMessage(&msg);
  1044.                 }
  1045.  
  1046.          // Get the caption
  1047.          GetWindowText(hWndSelect, gszWindowText, 100);
  1048.  
  1049.          // Capture the screen -- if we selected CLIENT only capture, then
  1050.          // select that open when calling our API.  Otherwise, get the
  1051.          // entire window.
  1052.  
  1053.          hBitmap = CopyWindowToBitmap(hWndSelect,
  1054.                     (wCommand == IDM_CAPTCLIENT) ? PW_CLIENT : PW_WINDOW);
  1055.  
  1056.          if (hBitmap)
  1057.             hPal = GetSystemPalette();
  1058.          else
  1059.             hPal = NULL;
  1060.       }
  1061.          break;
  1062.  
  1063.       default:
  1064.          /*
  1065.           * Oops, something went wrong
  1066.           */
  1067.          MessageBox(NULL, "INTERNAL ERROR 23: Invalid command",
  1068.                     szAppName, MB_ICONHAND | MB_OK);
  1069.          break;
  1070.          }
  1071.  
  1072.       if (hBitmap)
  1073.       {
  1074.          ghBitmap = hBitmap;
  1075.          hBitmap = NULL;
  1076.       }
  1077.       if (hPal)
  1078.       {
  1079.          ghPal = hPal;
  1080.          hPal = NULL;
  1081.       }
  1082.  
  1083.       // Now, paint our bitmap in the client area
  1084.       if (bStartup)
  1085.          bStartup = FALSE;
  1086.       InvalidateRect(hWnd, NULL, FALSE);
  1087.       UpdateWindow(hWnd);
  1088.  
  1089.       return;
  1090.  
  1091. }
  1092.  
  1093.  
  1094. //*********************************************************************
  1095. //
  1096. // SelectWindow()
  1097. //
  1098. // This function allows the user to select a window on the screen.  The
  1099. // cursor is changed to a custom cursor, then the user clicks on the title
  1100. // bar of a window to capture, and the handle to this window is returned.
  1101. //
  1102. // History:   Date      Author         Reason
  1103. //            9/15/91   Mark Bader     Created based on DIBVIEW code
  1104. //
  1105. //*********************************************************************
  1106.  
  1107. HWND SelectWindow()
  1108. {
  1109.    HCURSOR hOldCursor;     // Handle to old cursor
  1110.    POINT pt;               // Stores mouse position on a mouse click
  1111.    HWND hWndClicked;       // Window we clicked on
  1112.    MSG msg;
  1113.  
  1114.    /*
  1115.     * Capture all mouse messages
  1116.     */
  1117.    SetCapture(ghWndMain);
  1118.  
  1119.    /*
  1120.     * Load custom Cursor
  1121.     */
  1122.    hOldCursor = SetCursor(LoadCursor(ghInst, "SELECT"));
  1123.  
  1124.    /*
  1125.     * Eat mouse messages until a WM_LBUTTONUP is encountered.
  1126.     */
  1127.    for (;;)
  1128.    {
  1129.       WaitMessage();
  1130.       if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  1131.       {
  1132.          if (msg.message == WM_LBUTTONUP)
  1133.          {
  1134.             /*
  1135.              * Get mouse position
  1136.              */
  1137.             pt.x = LOWORD(msg.lParam);
  1138.             pt.y = HIWORD(msg.lParam);
  1139.  
  1140.             /*
  1141.              * Convert to screen coordinates
  1142.              */
  1143.             ClientToScreen(ghWndMain, &pt);
  1144.  
  1145.             /*
  1146.              * Get Window that we clicked on
  1147.              */
  1148.             hWndClicked = WindowFromPoint(pt);
  1149.  
  1150.             /*
  1151.              * If it's not a valid window, just return NULL
  1152.              */
  1153.             if (!hWndClicked)
  1154.             {
  1155.                ReleaseCapture();
  1156.                SetCursor(hOldCursor);
  1157.                return NULL;
  1158.             }
  1159.             break;
  1160.          }
  1161.       }
  1162.       else
  1163.          continue;
  1164.    }
  1165.    ReleaseCapture();
  1166.    SetCursor(hOldCursor);
  1167.    return (hWndClicked);
  1168. }
  1169.  
  1170.  
  1171. //**********************************************************************
  1172. //
  1173. // RubberBandScreen()
  1174. //
  1175. // This function allows the user to rubber-band a portion of the screen.
  1176. // When the left button is released, the rect that the user selected
  1177. // (in screen coordinates) is returned in lpRect.
  1178. //
  1179. // History:   Date      Author         Reason
  1180. //            9/15/91   Mark Bader     Created from SHOWDIB code
  1181. //
  1182. //*********************************************************************
  1183.  
  1184. void RubberBandScreen(LPRECT lpRect)
  1185. {
  1186.    POINT pt;           // Temporary POINT
  1187.    MSG msg;            // Used in our PeekMessage() loop
  1188.    POINT ptOrigin;     // Point where the user pressed left mouse button down
  1189.    RECT rcClip;        // Current selection
  1190.    HDC hScreenDC;      // DC to the screen (so we can draw on it)
  1191.    HCURSOR hOldCursor; // Saves old cursor
  1192.    BOOL bCapturing = FALSE;    // TRUE if we are rubber-banding
  1193.  
  1194.    hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  1195.  
  1196.    /*
  1197.     * Make cursor our custom cursor
  1198.     */
  1199.    hOldCursor = SetCursor(LoadCursor(NULL, IDC_CROSS));
  1200.  
  1201.    /*
  1202.     * Capture all mouse messages
  1203.     */
  1204.    SetCapture(ghWndMain);
  1205.  
  1206.    /* Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
  1207.     * continue to draw a rubberbanding rectangle and display it's dimensions
  1208.     */
  1209.    for (;;)
  1210.    {
  1211.       WaitMessage();
  1212.       if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  1213.       {
  1214.          /*
  1215.           * If the message is a WM_LBUTTONDOWN, begin drawing the
  1216.           * rubber-band box.
  1217.           */
  1218.          if (msg.message == WM_LBUTTONDOWN)
  1219.          {
  1220.             /*
  1221.              * User pressed left button, initialize selection
  1222.              * Set origin to current mouse position (in window coords)
  1223.              */
  1224.             ptOrigin.x = LOWORD(msg.lParam);
  1225.             ptOrigin.y = HIWORD(msg.lParam);
  1226.  
  1227.             /*
  1228.              * Convert to screen coordinates
  1229.              */
  1230.             ClientToScreen(ghWndMain, &ptOrigin);
  1231.  
  1232.             /*
  1233.              * rcClip is the current rectangle that the user
  1234.              * has selected.  Since user just pressed left button down,
  1235.              * initialize this rect very small
  1236.              */
  1237.             rcClip.left = rcClip.right = ptOrigin.x;
  1238.             rcClip.top = rcClip.bottom = ptOrigin.y;
  1239.             NormalizeRect(&rcClip);     // Make sure it is a normal rect
  1240.             DrawSelect(hScreenDC, TRUE, &rcClip); // Draw the rubber-band box
  1241.             bCapturing = TRUE;
  1242.          }
  1243.          /*
  1244.           * Any messages that make it into the next statement are mouse
  1245.           * messages, and we are capturing, so let's update the rubber-band
  1246.           * box
  1247.           */
  1248.          if (bCapturing)
  1249.          {
  1250.             DrawSelect(hScreenDC, FALSE, &rcClip);    // erase old rubber-band
  1251.             rcClip.left = ptOrigin.x;     // Update rect with new mouse info
  1252.             rcClip.top = ptOrigin.y;
  1253.             pt.x = LOWORD(msg.lParam);
  1254.             pt.y = HIWORD(msg.lParam);
  1255.  
  1256.             /*
  1257.              * Convert to screen coordinates
  1258.              */
  1259.             ClientToScreen(ghWndMain, &pt);
  1260.             rcClip.right = pt.x;
  1261.             rcClip.bottom = pt.y;
  1262.             NormalizeRect(&rcClip);
  1263.             DrawSelect(hScreenDC, TRUE, &rcClip); // And draw the new rubber-band
  1264.          }
  1265.  
  1266.          // If the message is WM_LBUTTONUP, then we stop the selection
  1267.          // process.
  1268.          if (msg.message == WM_LBUTTONUP)
  1269.          {
  1270.             DrawSelect(hScreenDC, FALSE, &rcClip);    // erase rubber-band
  1271.             SetCursor(hOldCursor);
  1272.             break;
  1273.          }
  1274.       }
  1275.       else
  1276.          continue;
  1277.    }
  1278.    ReleaseCapture();
  1279.    DeleteDC(hScreenDC);
  1280.  
  1281.    /*
  1282.     * Assign rect user selected to lpRect parameter
  1283.     */
  1284.    CopyRect(lpRect, &rcClip);
  1285. }
  1286.  
  1287.  
  1288. //***************************************************************************
  1289. //
  1290. // DrawSelect
  1291. //
  1292. // Draws the selected clip rectangle with its dimensions on the DC
  1293. //
  1294. // History:   Date      Author      Reason
  1295. //            9/15/91   Mark Bader  Created from SHOWDIB code
  1296. //
  1297. //***************************************************************************
  1298.  
  1299.  
  1300. void DrawSelect(HDC hdc,           // DC to draw on
  1301.                 BOOL fDraw,        // TRUE to draw, FALSE to erase
  1302.                 LPRECT lprClip)    // rect to draw
  1303. {
  1304.    char sz[80];
  1305.    DWORD dw;
  1306.    int x, y, len, dx, dy;
  1307.    HDC hdcBits;
  1308.    HBITMAP hbm;
  1309.    RECT rcClip;
  1310.  
  1311.    rcClip = *lprClip;
  1312.    if (!IsRectEmpty(&rcClip))
  1313.    {
  1314.  
  1315.       /* If a rectangular clip region has been selected, draw it */
  1316.       PatBlt(hdc, rcClip.left, rcClip.top, rcClip.right - rcClip.left, 1,
  1317.              DSTINVERT);
  1318.       PatBlt(hdc, rcClip.left, rcClip.bottom, 1, -(rcClip.bottom - rcClip.top)
  1319.              , DSTINVERT);
  1320.       PatBlt(hdc, rcClip.right - 1, rcClip.top, 1, rcClip.bottom - rcClip.top,
  1321.              DSTINVERT);
  1322.       PatBlt(hdc, rcClip.right, rcClip.bottom - 1, -(rcClip.right -
  1323.              rcClip.left), 1, DSTINVERT);
  1324.  
  1325.       /* Format the dimensions string ...*/
  1326.       wsprintf(sz, "%dx%d", rcClip.right - rcClip.left, rcClip.bottom -
  1327.                rcClip.top);
  1328.       len = lstrlen(sz);
  1329.  
  1330.       /* ... and center it in the rectangle */
  1331.       dw = GetTextExtent(hdc, sz, len);
  1332.       dx = LOWORD (dw);
  1333.       dy = HIWORD (dw);
  1334.       x = (rcClip.right + rcClip.left - dx) / 2;
  1335.       y = (rcClip.bottom + rcClip.top - dy) / 2;
  1336.       hdcBits = CreateCompatibleDC(hdc);
  1337.       SetTextColor(hdcBits, 0xFFFFFFL);
  1338.       SetBkColor(hdcBits, 0x000000L);
  1339.  
  1340.       /* Output the text to the DC */
  1341.       if (hbm = CreateBitmap(dx, dy, 1, 1, NULL))
  1342.       {
  1343.          hbm = SelectObject(hdcBits, hbm);
  1344.          ExtTextOut(hdcBits, 0, 0, 0, NULL, sz, len, NULL);
  1345.          BitBlt(hdc, x, y, dx, dy, hdcBits, 0, 0, SRCINVERT);
  1346.          hbm = SelectObject(hdcBits, hbm);
  1347.          DeleteObject(hbm);
  1348.       }
  1349.       DeleteDC(hdcBits);
  1350.    }
  1351. }
  1352.  
  1353.  
  1354. //*************************************************************************
  1355. //
  1356. //  FUNCTION   : NormalizeRect(RECT *prc)
  1357. //
  1358. //  PURPOSE    : If the rectangle coordinates are reversed, swaps them.
  1359. //               This is used to make sure that the first coordinate of
  1360. //               our rect is the upper left, and the second is lower right.
  1361. //
  1362. // History:   Date      Author      Reason
  1363. //            9/15/91   Mark Bader  Created from SHOWDIB code
  1364. //
  1365. //*************************************************************************
  1366.  
  1367. void PASCAL NormalizeRect(LPRECT prc)
  1368. {
  1369.    if (prc->right  < prc->left) SWAP(prc->right,  prc->left);
  1370.    if (prc->bottom < prc->top)  SWAP(prc->bottom, prc->top);
  1371. }
  1372.  
  1373.  
  1374. //***************************************************************************
  1375. //
  1376. // Function: DoSize
  1377. //
  1378. // Purpose:  Called by WndProc() on WM_SIZE
  1379. //
  1380. //           When the window is sized -- set up the scroll bars.
  1381. //
  1382. //           The window will be repainted if the new size, combined
  1383. //           with the current scroll bar positions would create blank
  1384. //           space at the left or bottom of the window.
  1385. //
  1386. // History:  Date      Author               Reason
  1387. //           10/15/91  Garrett McAuliffe    Cut code out from WM_SIZE case.
  1388. //           12/20/91  Patrick Schreiber    Modified for WinCap (bitmap instead
  1389. //                                          of DIB)
  1390. //
  1391. //***************************************************************************
  1392.  
  1393. void DoSize(HWND hWnd)
  1394. {
  1395.    BITMAP      bm;                     // Bitmap info structure
  1396.    int         cxBitmap=0, cyBitmap=0; // Bitmap width and height
  1397.    int         cxScroll, cyScroll;     // Scroll positions
  1398.    RECT        rect;                   // Client rectangle
  1399.  
  1400.    // Check to see we are displaying a bitmap,
  1401.    // if so, then repaint if necessary
  1402.    if (ghBitmap)
  1403.       {
  1404.       // Get info about bitmap
  1405.       GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
  1406.  
  1407.       // Get the width and height of the bitmap
  1408.       cxBitmap = bm.bmWidth;
  1409.       cyBitmap = bm.bmHeight;
  1410.  
  1411.       // Find out the dimensions of the window, and the current
  1412.       // thumb positions
  1413.       GetClientRect(hWnd, &rect);
  1414.  
  1415.       cxScroll = GetScrollPos (hWnd, SB_HORZ);
  1416.       cyScroll = GetScrollPos (hWnd, SB_VERT);
  1417.  
  1418.       // If current thumb positions would cause blank space
  1419.       // at right or bottom of client area, repaint
  1420.       if (cxScroll + rect.right > cxBitmap ||
  1421.           cyScroll + rect.bottom > cyBitmap)
  1422.          InvalidateRect(hWnd, NULL, FALSE);
  1423.  
  1424.       // Make sure scroll bars are updated
  1425.       SetupScrollBars(hWnd, cxBitmap, cyBitmap);
  1426.       }
  1427.    else if (bStartup)
  1428.       {
  1429.       // Need to update entire client area for cool logo
  1430.       InvalidateRect(hWnd, NULL, TRUE);
  1431.       }
  1432. }
  1433.  
  1434.  
  1435. //***************************************************************************
  1436. //
  1437. // Function: ReallyGetClientRect
  1438. //
  1439. // Purpose:  Gets the rectangular area of the client rect including
  1440. //           the area underneath visible scroll bars.  Stolen from
  1441. //           ShowDIB.
  1442. //
  1443. // History:   Date      Author      Reason
  1444. //            6/1/91    Garrett McAuliffe    Created.
  1445. //
  1446. //***************************************************************************
  1447.  
  1448. void ReallyGetClientRect(HWND hWnd, LPRECT lpRect)
  1449. {
  1450.    DWORD dwWinStyle;
  1451.  
  1452.    dwWinStyle = GetWindowLong (hWnd, GWL_STYLE);
  1453.  
  1454.    GetClientRect (hWnd, lpRect);
  1455.  
  1456.    if (dwWinStyle & WS_HSCROLL)
  1457.       lpRect->bottom += (GetSystemMetrics (SM_CYHSCROLL) - 1);
  1458.  
  1459.    if (dwWinStyle & WS_VSCROLL)
  1460.       lpRect->right  += (GetSystemMetrics (SM_CXVSCROLL) - 1);
  1461. }
  1462.  
  1463.  
  1464. //***************************************************************************
  1465. //
  1466. // Function: SetupScrollBars
  1467. //
  1468. // Purpose:  Sets up scroll bars.
  1469. //
  1470. // History:  Date       Author                Reason
  1471. //           6/1/91     Garrett McAuliffe     Created
  1472. //           12/20/91   Patrick Schreiber     Modified for WinCap
  1473. //
  1474. //***************************************************************************
  1475.  
  1476. void SetupScrollBars(HWND hWnd, WORD cxBitmap, WORD cyBitmap)
  1477. {
  1478.    RECT        rect;                         // Client Rectangle
  1479.    BOOL        bNeedScrollBars=FALSE;        // Need Scroll bars?
  1480.    unsigned    cxWindow, cyWindow;           // Width and height of client area
  1481.    int         cxRange=0, cyRange=0;         // Range needed for horz and vert
  1482.  
  1483.    // Do some initialization
  1484.    ReallyGetClientRect(hWnd, &rect);
  1485.  
  1486.    cxWindow = rect.right - rect.left;
  1487.    cyWindow = rect.bottom - rect.top;
  1488.  
  1489.    // Now determine if we need the scroll bars
  1490.    if ((cxWindow < (unsigned)cxBitmap) || (cyWindow < (unsigned)cyBitmap))
  1491.       bNeedScrollBars = TRUE;
  1492.  
  1493.  
  1494.    /* Setup the scroll bar ranges.  We want to be able to
  1495.     * scroll the window so that all the bitmap can appear
  1496.     * within the client area.  Take into account that
  1497.     * if the opposite scroll bar is activated, it eats
  1498.     * up some client area.
  1499.     */
  1500.  
  1501.    if (bNeedScrollBars)
  1502.       {
  1503.       cyRange = (unsigned)cyBitmap - cyWindow - 1 +
  1504.                 GetSystemMetrics (SM_CYHSCROLL);
  1505.       cxRange = (unsigned)cxBitmap - cxWindow - 1 +
  1506.                 GetSystemMetrics (SM_CXVSCROLL);
  1507.       }
  1508.  
  1509.    // Set the ranges we've calculated (0 to 0 means invisible scrollbar)
  1510.    SetScrollRange(hWnd, SB_VERT, 0, cyRange, TRUE);
  1511.    SetScrollRange(hWnd, SB_HORZ, 0, cxRange, TRUE);
  1512. }
  1513.  
  1514.  
  1515. //**********************************************************************
  1516. //
  1517. // Function:   DoScroll()
  1518. //
  1519. // Purpose:    Called by ChildWndProc() on WM_HSCROLL and WM_VSCROLL.
  1520. //             Window needs to be scrolled (user has clicked on one
  1521. //             of the scroll bars.
  1522. //
  1523. //             Does scrolling in both horiziontal and vertical directions.
  1524. //             Note that the variables are all named as if we were
  1525. //             doing a horizontal scroll.  However, if we're doing a
  1526. //             vertical scroll, they are initialized to the appropriate
  1527. //             values for a vertical scroll.
  1528. //
  1529. //             If we scroll by one (i.e. user clicks on one of the
  1530. //             scrolling arrows), we scroll the window by 1/SCROLL_RATIO
  1531. //             of the client area.  In other words, if SCROLL_RATION==4,
  1532. //             then we move the client area over a 1/4 of the width/height
  1533. //             of the screen.
  1534. //
  1535. //             If the user is paging up/down we move a full client area's
  1536. //             worth.
  1537. //
  1538. //             If the user moves the thumb to an absolute position, we
  1539. //             just move there.
  1540. //
  1541. //             ScrollWindow/re-painting do the actual work of scrolling.
  1542. //
  1543. // History:   Date      Author               Reason
  1544. //            10/15/91  Garrett McAuliffe    Cut code out from WM_?SCROLL case.
  1545. //
  1546. //**********************************************************************
  1547.  
  1548. void DoScroll(HWND hWnd, int message, WORD wPos, WORD wScrollType)
  1549. {
  1550.    int  xBar;                       // Where scrollbar is now.
  1551.    int  nMin;                       // Minumum scroll bar value.
  1552.    int  nMax;                       // Maximum scroll bar value.
  1553.    int  dx;                         // How much to move.
  1554.    int  nOneUnit;                   // # of pixels for LINEUP/LINEDOWN
  1555.    int  cxClient;                   // Width of client area.
  1556.    int  nHorzOrVert;                // Doing the horizontal or vertical?
  1557.    RECT rect;                       // Client area.
  1558.  
  1559.    GetClientRect (hWnd, &rect);
  1560.  
  1561.    if (message == WM_HSCROLL)
  1562.       {
  1563.       nHorzOrVert = SB_HORZ;
  1564.       cxClient    = rect.right - rect.left;
  1565.       }
  1566.    else
  1567.       {
  1568.       nHorzOrVert = SB_VERT;
  1569.       cxClient    = rect.bottom - rect.top;
  1570.       }
  1571.  
  1572.       // One a SB_LINEUP/SB_LINEDOWN we will move the DIB by
  1573.       //  1/SCROLL_RATIO of the client area (i.e. if SCROLL_RATIO
  1574.       //  is 4, it will scroll the DIB a quarter of the client
  1575.       //  area's height or width.
  1576.  
  1577.    nOneUnit = cxClient / SCROLL_RATIO;
  1578.    if (!nOneUnit)
  1579.       nOneUnit = 1;
  1580.  
  1581.    xBar = GetScrollPos (hWnd, nHorzOrVert);
  1582.    GetScrollRange (hWnd, nHorzOrVert, &nMin, &nMax);
  1583.  
  1584.    switch (wScrollType)
  1585.       {
  1586.       case SB_LINEDOWN:             // One line right.
  1587.          dx = nOneUnit;
  1588.          break;
  1589.  
  1590.       case SB_LINEUP:               // One line left.
  1591.          dx = -nOneUnit;
  1592.          break;
  1593.  
  1594.       case SB_PAGEDOWN:             // One page right.
  1595.          dx = cxClient;
  1596.          break;
  1597.  
  1598.       case SB_PAGEUP:               // One page left.
  1599.          dx = -cxClient;
  1600.          break;
  1601.  
  1602.       case SB_THUMBPOSITION:        // Absolute position.
  1603.          dx = wPos - xBar;
  1604.          break;
  1605.  
  1606.       default:                      // No change.
  1607.          dx = 0;
  1608.          break;
  1609.       }
  1610.  
  1611.    if (dx)
  1612.       {
  1613.       xBar += dx;
  1614.  
  1615.       if (xBar < nMin)
  1616.          {
  1617.          dx  -= xBar - nMin;
  1618.          xBar = nMin;
  1619.          }
  1620.  
  1621.       if (xBar > nMax)
  1622.          {
  1623.          dx  -= xBar - nMax;
  1624.          xBar = nMax;
  1625.          }
  1626.  
  1627.       if (dx)
  1628.          {
  1629.          SetScrollPos (hWnd, nHorzOrVert, xBar, TRUE);
  1630.  
  1631.          if (nHorzOrVert == SB_HORZ)
  1632.             ScrollWindow (hWnd, -dx, 0, NULL, NULL);
  1633.          else
  1634.             ScrollWindow (hWnd, 0, -dx, NULL, NULL);
  1635.  
  1636.          UpdateWindow (hWnd);
  1637.          }
  1638.       }
  1639. }
  1640.  
  1641. //****************************************************************************
  1642. //
  1643. // Function: DoPaint()
  1644. //
  1645. // Purpose:  Called by WndProc. Does painting for client area.
  1646. //
  1647. //
  1648. // History:  Date      Author      Reason
  1649. //
  1650. //           12/20/91  Patrick Schreiber      Created
  1651. //
  1652. //***************************************************************************
  1653.  
  1654. void DoPaint(HWND hWnd)
  1655. {
  1656.    HDC         hDC, hMemDC;         // Handle to DC, memory DC
  1657.    PAINTSTRUCT ps;                  // Painting structure
  1658.    BITMAP      bm;                  // BITMAP structure
  1659.    HBITMAP     hOldBm;              // Handle to previous bitmap
  1660.    RECT        rectClient, rectDDB; // Client and bitmap rectangles
  1661.    int         xScroll, yScroll;    // Scroll positions
  1662.    int         x, y;                // Logo origin
  1663.  
  1664.  
  1665.    // Begin painting
  1666.    hDC = BeginPaint(hWnd, &ps);
  1667.  
  1668.    // Check to see if we are displaying a bitmap
  1669.    if (!ghBitmap)
  1670.       {
  1671.  
  1672.       // No bitmap yet, are we in start mode?
  1673.       if (bStartup)
  1674.          {
  1675.          GetClientRect(hWnd, &rectClient);
  1676.  
  1677.          hMemDC = CreateCompatibleDC(ps.hdc);
  1678.  
  1679.          // Select our logo bitmap
  1680.          hOldBm = SelectObject(hMemDC, ghbmLogo);
  1681.  
  1682.          GetObject(ghbmLogo, sizeof(BITMAP), (VOID FAR *)&bm);
  1683.  
  1684.          x = (rectClient.right - bm.bmWidth) / 2;
  1685.          y = (rectClient.bottom - bm.bmHeight) / 2;
  1686.  
  1687.          // Now bitblt our logo to client area
  1688.          BitBlt(ps.hdc, x, y, bm.bmWidth, bm.bmHeight,
  1689.                 hMemDC, 0, 0, SRCCOPY);
  1690.  
  1691.          // Clean up
  1692.          SelectObject(hMemDC,hOldBm);
  1693.          DeleteDC(hMemDC);
  1694.          }
  1695.       else
  1696.          {
  1697.          // Turn off scroll bars in case they were on
  1698.          SetScrollRange (hWnd, SB_VERT, 0, 0, TRUE);
  1699.          SetScrollRange (hWnd, SB_HORZ, 0, 0, TRUE);
  1700.          }
  1701.       }
  1702.    else // We are displaying a bitmap
  1703.       {
  1704.       // Get bitmap info
  1705.       GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
  1706.  
  1707.       // Get scroll bar positions
  1708.       xScroll  = GetScrollPos  (hWnd, SB_HORZ);
  1709.       yScroll  = GetScrollPos  (hWnd, SB_VERT);
  1710.  
  1711.       // Set up the scroll bars appropriately.
  1712.       SetupScrollBars(hWnd, bm.bmWidth, bm.bmHeight);
  1713.  
  1714.       // Set up the necessary rectangles -- i.e. the rectangle
  1715.       //  we're rendering into, and the rectangle in the bitmap
  1716.       GetClientRect (hWnd, &rectClient);
  1717.  
  1718.       rectDDB.left   = xScroll;
  1719.       rectDDB.top    = yScroll;
  1720.       rectDDB.right  = xScroll + rectClient.right - rectClient.left;
  1721.       rectDDB.bottom = yScroll + rectClient.bottom - rectClient.top;
  1722.  
  1723.       if (rectDDB.right > bm.bmWidth)
  1724.          {
  1725.          int dx;
  1726.  
  1727.          dx = bm.bmWidth - rectDDB.right;
  1728.  
  1729.          rectDDB.right     += dx;
  1730.          rectClient.right  += dx;
  1731.          }
  1732.  
  1733.       if (rectDDB.bottom > bm.bmHeight)
  1734.          {
  1735.          int dy;
  1736.  
  1737.          dy = bm.bmHeight - rectDDB.bottom;
  1738.  
  1739.          rectDDB.bottom    += dy;
  1740.          rectClient.bottom += dy;
  1741.          }
  1742.  
  1743.  
  1744.       // Go do the actual painting.
  1745.       PaintBitmap(hDC, &rectClient, ghBitmap, &rectDDB, ghPal);
  1746.       }
  1747.  
  1748.  
  1749.    EndPaint(hWnd, &ps);
  1750. }
  1751.